package net.fukure.android.pecabc.media;

import java.util.ArrayList;
import java.util.List;

import net.fukure.android.pecabc.listener.OnCameraListener;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback, AutoFocusCallback {

    private final static String LOG_TAG = "AndroidFFWMECameraView";
	private SurfaceHolder holder;
    private Camera camera;
    private boolean autoFocus = false;
	private int imageWidth = 320;
	private int imageHeight = 240;
	private int frameRate = 5;
	private boolean portrait = false;
    private List<OnCameraListener> listeners = new ArrayList<OnCameraListener>();
	public CameraView(Context context) {
	    super(context);
	    
	    holder = this.getHolder();
	    holder.addCallback(this);
	    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}

	public void setSize(int w, int h) {
		imageWidth = w;
		imageHeight = h;
	}
	
	public void setOrientation(boolean portrait){
		this.portrait = portrait;
	}
	
	public EncodeParam getParam(){
		EncodeParam param = new EncodeParam();
		param.width = imageWidth;
		param.height = imageHeight;
		param.framerate = frameRate;
		param.portrait = 0;
		param.bitrate = 320;
		return param;
	}
		
    public void changePreviewSize(int w, int h){
    	camera.setPreviewCallback(null);
    	camera.stopPreview();
    	camera.release();
    	imageWidth = w;
    	imageHeight = h;
    	surfaceCreated(holder);
    }
    
    private void setCameraFormat(){
        Camera.Parameters currentParams = camera.getParameters();
    	
		if (currentParams.getPreviewFormat() != ImageFormat.NV21){
			try{
				currentParams.setPreviewFormat(ImageFormat.NV21);                   
	            camera.setParameters(currentParams);
	            currentParams = camera.getParameters();
			}catch(Exception e){
				Log.d(LOG_TAG, "set preview format error "+e.getMessage());
			}
        }
    }
    
    private void setCameraWhiteBalance(){
    	try{
	    	Parameters parameters = camera.getParameters();
	    	parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
	    	//parameters.setColorEffect(Camera.Parameters.EFFECT_AQUA);
	    	parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
	    	camera.setParameters(parameters);
    	}catch(Exception e){
    		
    	}
    }
    
    private void setSupportedCameraPreviewSize(){
		Size size = null;
		List<Size> previewSizes = camera.getParameters().getSupportedPreviewSizes();
		for(int i=0;i<previewSizes.size();i++){
			if(size==null) size = previewSizes.get(i);
			if(previewSizes.get(i).width>=imageWidth){
				size = previewSizes.get(i);
			}
			Log.d(LOG_TAG, previewSizes.get(i).width + "," + previewSizes.get(i).height);
		}

		try {
			Camera.Parameters cameraParameters = camera.getParameters();
			cameraParameters.setPreviewSize(size.width, size.height);
			camera.setParameters(cameraParameters);

		}catch (Exception e) {
			Log.e(LOG_TAG,"size error "+e.getMessage());
		}
    }
    
    private void setCameraFocus(){
		try {
	    	Camera.Parameters param = camera.getParameters();
	    	List<?> foucslist = param.getSupportedFocusModes();
	    	for (int i=0;i < foucslist.size();i++) {
	    		Log.d("foucs", "type= " + foucslist.get(i));
	    	}
			if(autoFocus){
				param.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
				camera.setParameters(param);
				autoFocus = false;
				Log.d(LOG_TAG,"setCameraFocus manual");
			}else{
				try{
					param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
					camera.setParameters(param);
					autoFocus = true;
				}catch(Exception e){
					camera.autoFocus(null);
					autoFocus = false;
				}
				Log.d(LOG_TAG,"setCameraFocus auto");
			}
		}catch (Exception e) {
			Log.e(LOG_TAG,"setCameraFocus error "+e.getMessage());
		}
    }
    
    private void setCameraRotation(){
		int rot = portrait ? 90 : 0;
		camera.setDisplayOrientation(rot);
    }
    
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(LOG_TAG,"surface Created");
		
		try {
			camera = Camera.open();
			camera.setPreviewDisplay(holder);
			
			setCameraFormat();
			setSupportedCameraPreviewSize();
			setCameraRotation();
			setCameraWhiteBalance();
			setCameraFocus();

			//CameraInfo info = new CameraInfo();
		    //Camera.getCameraInfo(0, info);
	        Camera.Parameters currentParams = camera.getParameters();

	    	imageWidth = currentParams.getPreviewSize().width;
	    	imageHeight = currentParams.getPreviewSize().height;
	    	frameRate = currentParams.getPreviewFrameRate();
	    	int format = currentParams.getPreviewFormat();
	    	
			firePreviewSizeChange();
			
	    	Log.d(LOG_TAG,"Create Preview Framerate: " + frameRate);
	     	Log.d(LOG_TAG,"Create Preview imageWidth: " + imageWidth + " imageHeight: " + imageHeight);
	        //Log.d(LOG_TAG,"Create Orientation: "+info.orientation);
	        Log.d(LOG_TAG,"Create Preview Format: " + format);
	        
	        camera.startPreview();
			camera.setPreviewCallback(this);
		}
		catch (Exception e) {
			Log.e(LOG_TAG,"surfaceCreated error "+e.getMessage());
		}

    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

		try {
	        Camera.Parameters currentParams = camera.getParameters();
	        
	        Log.d(LOG_TAG,"Surface Changed: width " + width + " height: " + height);
	        Log.d(LOG_TAG,"Preview Framerate: " + currentParams.getPreviewFrameRate());
	        Log.d(LOG_TAG,"Preview imageWidth: " + currentParams.getPreviewSize().width + " imageHeight: " + currentParams.getPreviewSize().height);
	    	
	    	//imageWidth = currentParams.getPreviewSize().width;
	    	//imageHeight = currentParams.getPreviewSize().height;
	    	//frameRate = currentParams.getPreviewFrameRate();
	    	
			firePreviewSizeChange();
		}catch(Exception e){
			Log.e(LOG_TAG, "surfaceChanged error ", e);
		}
    }
    
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        try {
            camera.setPreviewCallback(null);
		    camera.stopPreview();
        } catch (RuntimeException e) {
        	Log.d(LOG_TAG,"surfaceDestroyed error "+e.getMessage());
        }
        try {
			camera.release();
			camera = null;
        } catch (RuntimeException e) {
        	Log.d(LOG_TAG,"surfaceDestroyed error "+e.getMessage());
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction()==MotionEvent.ACTION_DOWN) {
        	try {
        		setCameraFocus();
        	}catch(Exception e){
        		
        	}
        }
        return true;
    }

    @Override
    public void onPreviewFrame(byte[] b, Camera camera) {
       	fireCameraFrameEvent(b);
    	camera.addCallbackBuffer(b);
    }

	public void addCameraListener(OnCameraListener listener){
		listeners.remove(listener);
		listeners.add(listener);
	}

	public void removeCameraFrameListener(OnCameraListener listener){
		listeners.remove(listener);
	}

	public void fireCameraFrameEvent(byte[] data){
		for (int i = 0; i < listeners.size(); i++) {
			OnCameraListener listener = listeners.get(i);
			listener.onCameraPreview(data);
		}
	}

	public void firePreviewSizeChange(){
		for (int i = 0; i < listeners.size(); i++) {
			OnCameraListener listener = listeners.get(i);
			listener.onCameraPreviewSizeChanged(imageWidth, imageHeight);
		}
	}

	@Override
	public void onAutoFocus(boolean success, Camera camera) {
	}
    
}
